import logging
import os
from ctypes import *
import ctypes
import time
from zlgcan import *

#########################     加载同级目录下的dll    #########################
# dbclib = windll.LoadLibrary("./zdbc.dll")

current_dir = os.path.dirname(os.path.abspath(__file__))    # 获取当前文件所在目录
parent_dir = os.path.dirname(current_dir)   # 获取当前文件所在目录的上层目录
grand_parent_dir = os.path.dirname(parent_dir)  # 获取上层目录的再上一层目录
sys.path.append(grand_parent_dir)   # 将再上一层目录添加到 sys.path 中

#########################     DBC相关宏定义    #########################

ERR_SUCCESS            = 0
ERR_FAILED             = 1
ERR_MULTI_TRANSMITTING = 2      #
ERR_NOT_SUPPORT        = 3

INVALID_DBC_HANDLE = -1         # 无效的DBC句柄

_MAX_FILE_PATH_ = 260           # 最长文件路径
_DBC_NAME_LENGTH_ = 127         # 名称最长长度
_DBC_COMMENT_MAX_LENGTH_ = 127  # 注释最长长度
_DBC_UNIT_MAX_LENGTH_ = 23      # 单位最长长度
_DBC_SIGNAL_MAX_COUNT_ = 256    # 一个消息含有的信号的最大数目

MUTIPLEXER_NONE     = 0         # 不使用复用器
MUTIPLEXER_M_VALUE  = 1         # 复用信号，当复用器开关的值为multiplexer_value时，该信号有效
MUTIPLEXER_M_SWITCH = 2         # 复用器开关，一个DBC消息只能有一个信号为开关

FT_CAN = 0                      # CAN帧
FT_CANFD = 1                    # CANFD帧

ZDBC_FRAME_TYPE_ZLGCAN = 0      # 使用zlgcan库的can_frame 或者 canfd_frame
ZDBC_FRAME_TYPE_CONTROLCAN = 1  # 使用ControlCAN的VCI_CAN_OBJ

PROTOCOL_J1939 = 0              # J1939协议
PROTOCOL_OTHER = 1              # 其他协议, 如果DBC文件中未指定，默认为此协议

#########################     DBC相关结构体    #########################
class DBCSignal(Structure):
    _fields_ = [("nStartBit", c_uint32),        # 起始位
                ("nLen", c_uint32),             # 位长度
                ("nFactor", c_double),          # 转换因子
                ("nOffset", c_double),          # 转换偏移 实际值=原始值*nFactor+nOffset
                ("nMin", c_double),             # 最小值
                ("nMax", c_double),             # 最大值
                ("nRawValue", c_uint64),        # 原始值
                ("is_signed", c_bool),          # 是否有符号数据
                ("is_motorola", c_bool),        # 是否摩托罗拉格式
                ("multiplexer_type", c_uint8),  # 复用器类型
                ("val_type", c_uint8),          # 0:int, 1:float, 2:double
                ("multiplexer_value", c_uint32),                    # 复用器开关值为此值时信号有效
                ("unit", c_ubyte * (_DBC_UNIT_MAX_LENGTH_ + 1)),    # 单位
                ("strName", c_ubyte * (_DBC_NAME_LENGTH_ + 1)),     # 名称
                ("strComment", c_ubyte * (_DBC_COMMENT_MAX_LENGTH_ + 1)),   # 注释
                ("strValDesc", c_ubyte * (_DBC_NAME_LENGTH_ + 1)),          # 值描述
                ("initialValue", c_double),     # 初始值(原始值)
                ("initialValueValid", c_uint8)] # 初始值是否有效


class DBCMessage(Structure):
    _fields_ = [("nSignalCount", c_uint32),     # 信号数量
                ("nID", c_uint32),              # 消息ID
                ("nSize", c_uint32),            # 消息占的字节数目
                ("nCycleTime", c_double),       # 发送周期
                ("nExtend", c_uint8),           # 1:扩展帧, 0:标准帧
                ("vSignals", DBCSignal * _DBC_SIGNAL_MAX_COUNT_),           # 信号集合
                ("strName", c_ubyte * (_DBC_NAME_LENGTH_ + 1)),             # 名称
                ("strComment", c_ubyte * (_DBC_COMMENT_MAX_LENGTH_ + 1))]   # 注释


class FileInfo(Structure):
    _pack_ = 1
    _fields_ = [("strFilePath", c_ubyte * 261), # dbc文件路径，中文路径请使用GB2312编码
                ("merge", c_uint8)]             # 1:不清除现有的数据, 即支持加载多个文件；0：清除原来的数据


class ValDescPair(Structure):
    _pack_ = 1
    _fields_ = [("value", c_double),                            # 值
                ("desc", c_ubyte * (_DBC_NAME_LENGTH_ + 1))]    # 对应的含义

class COUNT(Structure):
    _fields_ = [("count", c_uint32)]

class DBCNetworkNode(Structure):
    _fields_ = [
        ("strName", c_ubyte * (_DBC_NAME_LENGTH_ + 1)),  # 节点名称
        ("comment", c_ubyte * (_DBC_COMMENT_MAX_LENGTH_ + 1))  # 节点注释
    ]


class DBCProcessor:
    def __init__(self):
        # 移除了全局的dbclib，只在类内部维护
        try:
            self.dbclib = windll.LoadLibrary("commDriver/zlg/zdbc.dll")
        except FileNotFoundError:
            # 若 commDriver/zlg/zdbc.dll 不存在，尝试加载 ./zdbc.dll
            self.dbclib = windll.LoadLibrary("./zdbc.dll")
        self.dbclib.ZDBC_Init.restype = c_int
        self.DBCHandle = self._init_dbc()

    def load_dbc_file(self, file_path: str) -> None:
        """加载DBC文件并设置协议"""
        fileINF = FileInfo()
        # 使用create_string_buffer优化路径编码
        gb2312_path = create_string_buffer(file_path.encode('gb2312'))
        fileINF.strFilePath[:len(gb2312_path)] = gb2312_path.raw
        fileINF.merge = 0
        
        if not self.dbclib.ZDBC_LoadFile(self.DBCHandle, byref(fileINF)):
            raise RuntimeError(f"DBC文件加载失败: {file_path}")
        if not self.dbclib.ZDBC_SetProtocol(self.DBCHandle, PROTOCOL_OTHER):
            raise RuntimeError("协议类型设置失败")

    def encode_message(self, msg: DBCMessage, frame_type: int = FT_CAN) -> ZCAN_Transmit_Data:
        if frame_type == 0:
            """编码消息为CAN帧"""
            # logging.info("编码消息为CAN帧")
            can_frame = ZCAN_CAN_FRAME()
            nCount = COUNT(count=1)
            
            if not self.dbclib.ZDBC_Encode(self.DBCHandle, byref(can_frame), byref(nCount), byref(msg), frame_type):
                raise RuntimeError("消息编码失败")
            
            return ZCAN_Transmit_Data(frame=can_frame, transmit_type=0)
        else:
            """编码消息为CANFD帧"""
            # logging.info("编码消息为CANFD帧")
            canfd_frame = ZCAN_CANFD_FRAME()
            nCount = COUNT(count=1)
            if not self.dbclib.ZDBC_Encode(self.DBCHandle, byref(canfd_frame), byref(nCount), byref(msg), frame_type):
                raise RuntimeError("消息编码失败")
            return ZCAN_TransmitFD_Data(frame=canfd_frame, transmit_type=0)

    def decode_frame(self, can_frame, frame_type: int = FT_CAN) -> DBCMessage:
        msg = DBCMessage()
        if self.dbclib.ZDBC_Decode(self.DBCHandle, byref(msg), byref(can_frame), 1, frame_type):
            return msg
        return None

    def print_all_messages(self):
        msg_count = self.dbclib.ZDBC_GetMessageCount(self.DBCHandle)
        print(f"DBC文件中消息总数: {msg_count}")
        
        msg = DBCMessage()
        if self.dbclib.ZDBC_GetFirstMessage(self.DBCHandle, byref(msg)):
            self._print_message(msg)
            while self.dbclib.ZDBC_GetNextMessage(self.DBCHandle, byref(msg)):
                self._print_message(msg)

    def _print_message(self, message: DBCMessage, print_signals: bool = True) -> None:
        self.print_dbc_message(message, print_signals)

    def print_dbc_message(self, message: DBCMessage, print_signals: bool) -> None:  # 修复参数名拼写错误
        """增强打印方法"""
        print(f"\nID: {hex(message.nID)} {'扩展帧' if message.nExtend else '标准帧'} 长度: {message.nSize}")
        print(f"周期时间: {message.nCycleTime}ms")
        print(f"消息名称: {self._decode_gb2312(message.strName)}")
        print(f"信号数量: {message.nSignalCount}")

        if not print_signals:
            return
            
        for i in range(message.nSignalCount):
            signal = message.vSignals[i]
            actual_value = self._calculate_actual_value(signal)  # 计算实际值
            print(f"\n信号 {i + 1} \t{self._decode_gb2312(signal.strName)}:")
            print(f"  实际值: {actual_value}{self._decode_gb2312(signal.unit)}")
            print(f"  原始值: {signal.nRawValue} 偏移: {signal.nOffset} 因子: {signal.nFactor}")
            print(f"  范围: [{signal.nMin}, {signal.nMax}] 起始位: {signal.nStartBit} 长度: {signal.nLen}")

    def _decode_gb2312(self, buffer) -> str:
        """解码GB2312字符串"""
        return bytes(buffer).split(b'\x00')[0].decode('gb2312', errors='replace')

    def _calculate_actual_value(self, signal: DBCSignal) -> float:
        """计算信号实际值"""
        # 根据数据类型处理有符号值
        raw = signal.nRawValue
        if signal.is_signed and signal.nLen > 0:
            max_val = 1 << (signal.nLen - 1)
            if raw >= max_val:
                raw -= (1 << signal.nLen)
        
        return raw * signal.nFactor + signal.nOffset

    def get_message_by_id(self, can_id):
        msg = DBCMessage()
        if self.dbclib.ZDBC_GetMessageById(self.DBCHandle, can_id, byref(msg)) != 0:
            return msg
        return None

    def _init_dbc(self):
        handle = self.dbclib.ZDBC_Init(0, 0)
        if handle == INVALID_DBC_HANDLE:
            raise Exception("DBC句柄初始化失败")
        return handle

    def __del__(self):
        if self.DBCHandle != INVALID_DBC_HANDLE:
            self.dbclib.ZDBC_Release(self.DBCHandle)

    def get_nodes(self) -> list[str]:
        """获取DBC文件中定义的所有节点"""
        node_count = c_uint32(0)
        # 使用指针参数获取节点数量
        node_count = self.dbclib.ZDBC_GetNetworkNodeCount(self.DBCHandle, byref(node_count))
        
        nodes = []
        node = DBCNetworkNode()  # 使用新定义的结构体
        for i in range(node_count):
            # 获取节点信息到结构体中
            if self.dbclib.ZDBC_GetNetworkNodeAt(self.DBCHandle, i, byref(node)):
                name = bytes(node.strName).split(b'\x00')[0].decode('gb2312')
                nodes.append(name)
        return nodes

    def update_signal_value(self, ID, signal_name: str, value: float, frame_type: int = FT_CAN, base_data: bytes = []) -> ZCAN_Transmit_Data:
        msg = self.get_message_by_id(ID)

        # 如果有基准数据，先解码到消息对象
        if base_data:
            # logging.info("解码基准数据")
            can_frame = ZCAN_CANFD_FRAME() if frame_type == FT_CANFD else ZCAN_CAN_FRAME()
            # 正确填充数据缓冲区
            if frame_type == FT_CANFD:
                can_frame.can_id = ID
                for i in range(len(base_data)):
                    can_frame.data[i] = base_data[i]
                can_frame.len = len(base_data)
                # logging.info(f"填充CANFD数据: {base_data[:can_frame.len]}")
            else:
                can_frame.can_id = ID
                for i in range(len(base_data)):
                    can_frame.data[i] = base_data[i]
                can_frame.can_dlc = len(base_data)
                # logging.info(f"填充CAN数据: {base_data[:can_frame.can_dlc]}")
            
            decoded_msg = self.decode_frame(can_frame, frame_type)
            if decoded_msg:
                # logging.info(f"基准数据解码成功，消息ID: {hex(decoded_msg.nID)}")
                msg = decoded_msg

        """更新指定信号的值"""
        target_signal = None
        encoded_name = signal_name.encode('gb2312')
        
        # 查找目标信号
        for i in range(msg.nSignalCount):
            signal = msg.vSignals[i]
            if bytes(signal.strName).startswith(encoded_name + b'\x00'):
                target_signal = signal
                # logging.info(f"找到信号: {signal_name}")
                break
                
        if not target_signal:
            return []

        # 计算原始值并考虑有符号类型
        raw_value = int((value - target_signal.nOffset) / target_signal.nFactor)
        
        # 处理有符号数值
        if target_signal.is_signed and target_signal.nLen > 0:
            max_val = 1 << (target_signal.nLen - 1)
            if raw_value < -max_val or raw_value >= max_val:
                logging.error("信号值超出范围")
                raise ValueError("信号值超出范围")
                
            target_signal.nRawValue = c_uint64(raw_value & ((1 << target_signal.nLen) - 1)).value
        else:
            if raw_value < 0 or raw_value >= (1 << target_signal.nLen):
                logging.error("信号值超出范围")
                raise ValueError("信号值超出范围")  
            target_signal.nRawValue = raw_value 
        
        
        trans_data = None
        trans_data = self.encode_message(msg, frame_type)
            
        return trans_data

    def refresh_message(self, can_id: int) -> DBCMessage:
        """刷新并获取最新的消息结构"""
        if msg := self.get_message_by_id(can_id):
            return msg
        raise ValueError(f"未找到ID为 {hex(can_id)} 的消息")
    
    def parse_messages_to_list(self, frame_type: int = FT_CAN) -> list:
        """解析所有消息为结构化列表，包含ID、数据、周期和CANFD标识"""
        msg_list = []
        msg = DBCMessage()
        
        # 遍历所有消息
        if not self.dbclib.ZDBC_GetFirstMessage(self.DBCHandle, byref(msg)):
            return msg_list
            
        while True:
            try:
                # 编码获取数据帧
                trans_data = self.encode_message(msg, frame_type)
                # 将数据转换为十六进制字符串
                data_bytes = trans_data.frame.data[:msg.nSize]
                # data_hex = ''.join(f"{b:02X}" for b in data_bytes)
            except RuntimeError:
                data_hex = "编码失败"
            
            # 构建消息信息字典
            msg_info = {
                "id": msg.nID,
                "data": data_bytes, #data_hex,
                "cycle_ms": msg.nCycleTime,
                "is_canfd": frame_type == FT_CANFD,
                "dlc": msg.nSize,
                "signals": []
            }
            
            # 添加信号基本信息
            for i in range(msg.nSignalCount):
                signal = msg.vSignals[i]
                msg_info["signals"].append({
                    "name": self._decode_gb2312(signal.strName),
                    "raw_value": signal.nRawValue,
                    "physical_value": self._calculate_actual_value(signal)
                })
            
            msg_list.append(msg_info)
            
            # 获取下一条消息
            if not self.dbclib.ZDBC_GetNextMessage(self.DBCHandle, byref(msg)):
                break
                
        return msg_list

    

if __name__ == "__main__":
    # 增强测试用例
    processor = DBCProcessor()
    try:
        dbc_file = "E:/project/03_xiaomi/testscript/xiaomi/data/Kunlun_ETC_BodyCANFD.dbc"
        processor.load_dbc_file(dbc_file)
        print("\n解析消息为结构化列表:")
        msg_list = processor.parse_messages_to_list(1)
        for idx, msg in enumerate(msg_list):  # 示例显示前3条
            print(f"\n消息 {idx+1}:")
            data_hex = ' '.join(f"{b:02X}" for b in msg['data'])
            print(f"ID: 0x{msg['id']:03X} | 数据: {data_hex}")
            print(f"周期: {msg['cycle_ms']}ms | CANFD: {msg['is_canfd']}")
            print(f"包含信号: {[s['name'] for s in msg['signals']]}")
        # processor.print_all_messages()
        
        # if (msg := processor.get_message_by_id(0x243)):
        #     print("\n正在编码测试消息...")
        #     trans_data = processor.encode_message(msg)
            
        #     print("\n正在解码测试帧...")
        #     decoded_msg = processor.decode_frame(trans_data.frame)
        #     if decoded_msg:
        #         processor.print_dbc_message(decoded_msg, print_signals=True)

        # # 获取所有节点
        # nodes = processor.get_nodes()
        # print(f"DBC节点列表: {', '.join(nodes)}")

        # # 更新信号值
        # if (msg := processor.get_message_by_id(0x243)):
        #     # 更新名为"EngineSpeed"的信号值
        #     if processor.update_signal_value(msg, "EngineSpeed", 2500.5):
        #         # 编码发送更新后的消息
        #         trans_data = processor.encode_message(msg)
                
        #     # 刷新消息到初始状态
        #     msg = processor.refresh_message(0x243)

                
    except Exception as e:
        print(f"处理失败: {e}")